// 正确思路：使用动态窗口进行切割，也是运用了new Set() 加双指针      
// 但与我不同的是只生成了一次set，并且调用set.has api 进行判断，而不是简单用size进行判断
// 并且一次声明set一直使用，充分利用伸缩窗口，right 并不用重置成 right = left（这样做也行，但是效率低）


function lengthOfLongestSubstring(s) {
  let len = s.length;
  let result = 0;

  let set = new Set();
  // 左指针用来收缩窗口
  let left = 0;
  // 右指针用来扩张窗口
  let right = 0;

  while (left < len) {
    // 如果不重复，就不断扩张窗口，元素添加到set中
    while (right < len && !set.has(s[right])) {  // 如果具有相同的，set.has 返回true  , !set.has 为false 停止循环
      set.add(s[right]);
      right++;
    }
    // 到这里说明有元素重复了，先记录子串长度，然后收缩窗口
    result = Math.max(result, right - left);
    // 收缩窗口
    set.delete(s[left]); // 精妙之处！！！！！！       删除最左边的，但是set数组不动继续添加尝试，而不是像我一样每次生成new set,这样做set只需要声明一次在整个算法中使用
    left++; // 伸缩窗口
  }
  return result;
}